home *** CD-ROM | disk | FTP | other *** search
/ Mac Power 1997 January / macpower199701.bin / AMUG / Programming_10 / WASTE 1.3a1.sit / WASTE 1.3a1 Distribution / WASTE 1.3a1 / WEMouse.c < prev    next >
Encoding:
C/C++ Source or Header  |  1996-08-23  |  35.9 KB  |  1,440 lines  |  [TEXT/CWIE]

  1. /*
  2.  *    WEMouse.c
  3.  *
  4.  *    WASTE PROJECT
  5.  *  Mouse Clicks and Support for Drag and Drop
  6.  *
  7.  *  Copyright (c) 1993-1996 Marco Piovanelli
  8.  *    All Rights Reserved
  9.  *
  10.  *  C port by Dan Crevier
  11.  *
  12.  */
  13.  
  14.  
  15. #include "WASTEIntf.h"
  16.  
  17. #ifndef __FOLDERS__
  18. #include <Folders.h>
  19. #endif
  20.  
  21. #if WASTE_IC_SUPPORT
  22. #ifndef __ICTYPES__
  23. #include "ICTypes.h"
  24. #endif
  25. #ifndef __ICAPI__
  26. #include "ICAPI.h"
  27. #endif
  28. #endif // WASTE_IC_SUPPORT
  29.  
  30. //    static variables
  31.  
  32. static DragSendDataUPP _weFlavorSender = nil ;
  33.  
  34. pascal Boolean _WEIsOptionDrag ( DragReference drag )
  35. {
  36.     EventModifiers downModifiers, upModifiers ;
  37.  
  38.     // get drag modifiers
  39.     // ??? shouldn't GetDragModifiers take EventModifiers* parameters ???
  40.     GetDragModifiers ( drag, nil, ( SInt16 * ) & downModifiers, ( SInt16 * ) & upModifiers ) ;
  41.  
  42.     // return true if the option key was held down at the beginning and/or at the end
  43.     return ( ( ( downModifiers | upModifiers ) & optionKey ) != 0 ) ;
  44. }
  45.  
  46. pascal OSErr _WEGetFlavor ( DragReference drag, ItemReference dragItem,
  47.                 FlavorType requestedType, Handle hFlavor,
  48.                 SInt32 dropOffset, WEHandle hWE )
  49. {
  50.     // get the requested flavor out of the specified drag reference and put it into
  51.     // the given handle, if any -- if hFlavor is nil, just check whether the specified flavor
  52.     // is there or can be obtained by invoking a user-defined translation routine
  53.  
  54.     FlavorFlags flags ;
  55.     Size dataSize ;
  56.     WETranslateDragUPP translateDragHook ;
  57.     Boolean saveFlavorLock ;
  58.     OSErr err ;
  59.  
  60.     // see if the drag item has the requested flavor type,
  61.     // without forcing the actual data to be sent and/or translated
  62.     err = GetFlavorFlags ( drag, dragItem, requestedType, & flags ) ;
  63.     if ( err == badDragFlavorErr )
  64.     {
  65.         // requested flavor is not available: our client may try a custom translation
  66.         // this is especially handy to translate HFS objects like TEXT and PICT files
  67.         if ( ( translateDragHook = ( * hWE ) -> translateDragHook ) != nil )
  68.         {
  69.             err = CallWETranslateDragProc ( drag, dragItem, requestedType, hFlavor,
  70.                         dropOffset, hWE, translateDragHook ) ;
  71.         }
  72.     }
  73.     else if ( err == noErr )
  74.     {
  75.  
  76.         // requested flavor is available: get it if hFlavor is not nil
  77.         if ( hFlavor == nil )
  78.         {
  79.             return err ;
  80.         }
  81.  
  82.         // get size of flavor data
  83.         if ( ( err = GetFlavorDataSize( drag, dragItem, requestedType, & dataSize ) ) != noErr )
  84.         {
  85.             return err ;
  86.         }
  87.  
  88.         // resize the handle
  89.         SetHandleSize ( hFlavor, dataSize ) ;
  90.         if ( ( err = MemError ( ) ) != noErr )
  91.         {
  92.             return err ;
  93.         }
  94.  
  95.         // get flavor data
  96.         saveFlavorLock = _WESetHandleLock ( hFlavor, true ) ;
  97.         err = GetFlavorData ( drag, dragItem, requestedType, * hFlavor, & dataSize, 0 ) ;
  98.         _WESetHandleLock ( hFlavor, saveFlavorLock ) ;
  99.     }
  100.  
  101.     return err;
  102. }
  103.  
  104. pascal OSErr _WEExtractFlavor ( DragReference drag, ItemReference dragItem,
  105.                         FlavorType theType, Handle *hFlavor,
  106.                         SInt32 dropOffset, WEHandle hWE )
  107. {
  108.     OSErr err;
  109.  
  110.     // allocate a new handle
  111.     if ( ( err = _WEAllocate ( 0, kAllocTemp, hFlavor ) ) == noErr )
  112.     {
  113.         // put the requested flavor into this handle
  114.         if ( ( err = _WEGetFlavor ( drag, dragItem, theType, * hFlavor, dropOffset, hWE ) ) != noErr )
  115.         {
  116.  
  117.         // if an error occurred, forget the handle
  118.             _WEForgetHandle ( hFlavor ) ;
  119.         }
  120.     }
  121.  
  122.     return err ;
  123. }
  124.  
  125. pascal Boolean WECanAcceptDrag ( DragReference drag, WEHandle hWE )
  126. {
  127.     WEPtr pWE ;
  128.     UInt16 numDragItems ;
  129.     UInt16 dragItemIndex ;
  130.     ItemReference dragItem ;
  131.     Boolean saveWELock ;
  132.     OSErr err ;
  133.     Boolean retval = false ;
  134.  
  135.     // lock the WE record
  136.     saveWELock = _WESetHandleLock ( ( Handle ) hWE, true ) ;
  137.     pWE = * hWE ;
  138.  
  139.     // refuse all drags if the weFReadOnly feature is enabled
  140.     if ( BTST ( pWE -> features, weFReadOnly ) )
  141.         goto cleanup ;
  142.  
  143.     // count items in this drag
  144.     if ( ( err = CountDragItems ( drag, & numDragItems ) ) != noErr )
  145.         goto cleanup ;
  146.  
  147.     for ( dragItemIndex = 1 ; dragItemIndex <= numDragItems ; dragItemIndex ++ )
  148.     {
  149.         // get item reference number for current drag item
  150.         if ( ( err = GetDragItemReferenceNumber ( drag, dragItemIndex, & dragItem ) ) != noErr )
  151.             goto cleanup ;
  152.  
  153.         // see if this drag item contains a text flavor
  154.         err = _WEGetFlavor ( drag, dragItem, kTypeText, nil, kInvalidOffset, hWE ) ;
  155.  
  156. #if WASTE_OBJECTS
  157.         if ( err == badDragFlavorErr )
  158.         {
  159.             SInt32 objectIndex ;
  160.             FlavorType objectType ;
  161.  
  162.             // see if this drag item contains a flavor matching one of the registered object types
  163.             for ( objectIndex = 0 ;
  164.                   _WEGetIndObjectType ( objectIndex, & objectType, hWE ) == noErr ;
  165.                   objectIndex ++ )
  166.             {
  167.                 if ( ( err = _WEGetFlavor(drag, dragItem, objectType, nil, kInvalidOffset, hWE ) ) != badDragFlavorErr )
  168.                 {
  169.                     break ;
  170.                 }
  171.             }
  172.         }
  173. #endif
  174.  
  175.         if ( err != noErr )
  176.             goto cleanup ;
  177.     } // for
  178.  
  179.     retval = true;
  180.  
  181. cleanup:
  182.  
  183.     // unlock the WE record
  184.     _WESetHandleLock ( ( Handle ) hWE, saveWELock ) ;
  185.  
  186.     return retval ;
  187. }
  188.  
  189. pascal void _WEUpdateDragCaret ( SInt32 offset, WEHandle hWE )
  190. {
  191.     WEPtr pWE = * hWE ;        // assume WE record is already locked
  192.     UInt32 currentTime ;
  193.  
  194.     // get current time
  195.     currentTime = TickCount ( ) ;
  196.  
  197.     if ( offset == pWE -> dragCaretOffset )
  198.     {
  199.  
  200.         // drag caret offset didn't change; blink the caret
  201.         if ( ( currentTime > pWE -> caretTime + GetCaretTime ( ) ) && ( offset != kInvalidOffset ) )
  202.         {
  203.             _WEDrawCaret ( pWE -> dragCaretOffset, hilite, false, hWE ) ;
  204.             BCHG ( pWE -> flags, weFDragCaretVisible ) ;        // invert flag
  205.             pWE -> caretTime = currentTime ;
  206.         }
  207.     }
  208.     else
  209.     {
  210.  
  211.         // drag caret offset did change
  212.         // hide old caret, if it's showing
  213.         if ( BTST ( pWE -> flags, weFDragCaretVisible ) )
  214.         {
  215.             _WEDrawCaret ( pWE -> dragCaretOffset, hilite, false, hWE ) ;
  216.         }
  217.  
  218.         // show new caret (unless offset is kInvalidOffset)
  219.         if ( offset != kInvalidOffset )
  220.         {
  221.             _WEDrawCaret ( offset, hilite, false, hWE ) ;
  222.             BSET ( pWE -> flags, weFDragCaretVisible ) ;
  223.             pWE -> caretTime = currentTime ;
  224.         }
  225.         else
  226.         {
  227.             BCLR ( pWE -> flags, weFDragCaretVisible ) ;
  228.         }
  229.  
  230.         // remember drag caret offset
  231.         pWE -> dragCaretOffset = offset ;
  232.     }
  233. }
  234.  
  235. pascal OSErr WETrackDrag ( DragTrackingMessage message, DragReference drag, WEHandle hWE )
  236. {
  237.     WEPtr pWE ;
  238.     DragAttributes attributes ;
  239.     Point mouse ;
  240.     LongPt thePoint ;
  241.     SInt32 offset ;
  242.     WEEdge edge ;
  243.     Boolean saveWELock ;
  244.     OSErr err ;
  245.  
  246.     // lock the WE record
  247.     saveWELock = _WESetHandleLock ( ( Handle ) hWE, true ) ;
  248.     pWE = * hWE ;
  249.  
  250.     // dispatch on message
  251.     switch ( message )
  252.     {
  253.         case dragTrackingEnterWindow :
  254.             // determine whether we can accept this drag
  255.             if ( WECanAcceptDrag ( drag, hWE ) )
  256.             {
  257.                 BSET ( pWE -> flags, weFCanAcceptDrag ) ;
  258.             }
  259.             else
  260.             {
  261.                 BCLR ( pWE -> flags, weFCanAcceptDrag ) ;
  262.             }
  263.  
  264.             // reset clickTime
  265.             pWE -> clickTime = 0 ;
  266.             break ;
  267.  
  268.         case dragTrackingInWindow :
  269.             if ( BTST ( pWE -> flags, weFCanAcceptDrag ) )
  270.             {
  271.  
  272.                 // get drag attributes
  273.                 if ( ( err = GetDragAttributes ( drag, & attributes ) ) != noErr )
  274.                     goto cleanup;
  275.  
  276.                 // get current mouse location in local coordinates
  277.                 if ( ( err = GetDragMouse ( drag, & mouse, nil ) ) != noErr )
  278.                     goto cleanup;
  279.                 GlobalToLocal ( & mouse ) ;
  280.  
  281.                 if ( PtInRgn ( mouse, pWE -> viewRgn ) )
  282.                 {
  283.                     // mouse is in text area
  284.                     // hilite the text rectangle, if we haven't already
  285.                     // and if the drag has left sender window since drag tracking started
  286.                     if ( ( ! BTST ( pWE -> flags, weFHilited ) ) &&
  287.                          ( attributes & dragHasLeftSenderWindow ) )
  288.                     {
  289.                         CallWEHiliteDropAreaProc ( drag, true, hWE, pWE -> hiliteDropAreaHook ) ;
  290.                         BSET ( pWE -> flags, weFHilited ) ;
  291.                     }
  292.  
  293.                     // hide the regular caret
  294.                     if ( BTST ( pWE -> flags, weFCaretVisible ) )
  295.                     {
  296.                         _WEBlinkCaret ( hWE ) ;
  297.                     }
  298.  
  299.                     // get text offset corresponding to mouse location
  300.                     WEPointToLongPoint ( mouse, & thePoint ) ;
  301.                     offset = WEGetOffset ( & thePoint, & edge, hWE ) ;
  302.  
  303.                     // if offset is within the original selection range, don't display drag feedback
  304.                     if ( drag == pWE -> currentDrag )
  305.                     {
  306.                         if ( _WEOffsetInRange ( offset, edge, pWE -> selStart, pWE -> selEnd ) )
  307.                         {
  308.                             offset = kInvalidOffset ;
  309.                         }
  310.                     }
  311.  
  312.                     // provide a drag feedback in the form of a blinking caret
  313.                     _WEUpdateDragCaret ( offset, hWE ) ;
  314.  
  315.                     // clear clickTime
  316.                     pWE -> clickTime = 0 ;
  317.                 }
  318.                 else
  319.                 {
  320.  
  321.                     // mouse is outside text area
  322.                     // dehilite the text rectangle, if it's hilited
  323.                     if ( BTST ( pWE -> flags, weFHilited ) )
  324.                     {
  325.                         CallWEHiliteDropAreaProc ( drag, false, hWE, pWE->hiliteDropAreaHook ) ;
  326.                         BCLR ( pWE -> flags, weFHilited ) ;
  327.                     }
  328.  
  329.                     // hide the drag caret, if it's showing
  330.                     _WEUpdateDragCaret ( kInvalidOffset, hWE ) ;
  331.  
  332.                     // if the mouse has been remaining outside the view region for 10 ticks or more
  333.                     // and this drag was created by this WE instance, call the click loop routine
  334.                     if ( drag == pWE->currentDrag )
  335.                     {
  336.                         UInt32 currentTime = TickCount ( ) ;
  337.                         if ( pWE -> clickTime == 0 )
  338.                         {
  339.                             pWE -> clickTime = currentTime ;
  340.                         }
  341.                         else if ( currentTime > pWE -> clickTime + kAutoScrollDelay )
  342.                         {
  343.                             if ( pWE -> clickLoop != nil )
  344.                             {
  345.                                 CallWEClickLoopProc ( hWE, pWE -> clickLoop ) ;
  346.                             }
  347.                         }
  348.                     }
  349.                 }
  350.             }
  351.             break ;
  352.  
  353.         case dragTrackingLeaveWindow :
  354.  
  355.             // drag has left this window
  356.             // dehilite the text area if necessary
  357.             if ( BTST ( pWE -> flags, weFHilited ) )
  358.             {
  359.                 CallWEHiliteDropAreaProc ( drag, false, hWE, pWE->hiliteDropAreaHook ) ;
  360.                 BCLR ( pWE -> flags, weFHilited ) ;
  361.             }
  362.  
  363.             // hide the drag caret, if it's showing
  364.             _WEUpdateDragCaret ( kInvalidOffset, hWE ) ;
  365.             break;
  366.  
  367.         default:
  368.             ;
  369.     }
  370.  
  371.     // clear result code
  372.     err = noErr ;
  373.  
  374. cleanup:
  375.     // unlock the WE record
  376.     _WESetHandleLock ( ( Handle ) hWE, saveWELock ) ;
  377.  
  378.     // return result code
  379.     return err ;
  380. }
  381.  
  382. pascal OSErr WEReceiveDrag(DragReference drag, WEHandle hWE)
  383. {
  384.     WEPtr pWE;
  385.     WEActionHandle hAction;
  386.     Handle hText = nil;
  387.     Handle hStyles = nil;
  388. #if WASTE_OBJECTS
  389.     Handle hSoup = nil;
  390. #endif
  391.     Handle hObjectData = nil;
  392.     Point mouse;
  393.     LongPt dropLocation;
  394.     SInt32 insertionOffset;
  395.     SInt32 insertionLength;
  396.     SInt32 sourceStart;
  397.     SInt32 sourceEnd;
  398.     SInt32 destStart;
  399.     SInt32 destEnd;
  400.     SInt32 delta;
  401.     UInt16 dragItemIndex;
  402.     UInt16 numDragItems;
  403.     ItemReference dragItem;
  404.     GrafPtr savePort;
  405.     SInt16 intPasteAction;
  406.     WEEdge dropEdge;
  407.     char space = kSpace;
  408.     Boolean isMove = false;
  409.     Boolean isBackwards;
  410.     Boolean saveWELock;
  411.     OSErr err;
  412.  
  413.     // lock the WE record
  414.     saveWELock = _WESetHandleLock((Handle) hWE, true);
  415.     pWE = *hWE;
  416.  
  417.     // set up the port
  418.     GetPort(&savePort);
  419.     SetPort(pWE->port);
  420.  
  421.     // stop any ongoing inline input session
  422.     WEStopInlineSession(hWE);
  423.  
  424.     // hide the drag caret
  425.     _WEUpdateDragCaret(kInvalidOffset, hWE);
  426.  
  427.     // refuse this drag if it doesn't taste good
  428.     err = badDragFlavorErr;
  429.     if (!WECanAcceptDrag(drag, hWE))
  430.         goto cleanup;
  431.  
  432.     // get drop location in local coordinates
  433.     if ((err = GetDragMouse(drag, &mouse, nil)) != noErr)
  434.         goto cleanup;
  435.     GlobalToLocal(&mouse);
  436.  
  437.     // for the drag to be accepted, the drop location must be within the view region
  438.     err = dragNotAcceptedErr;
  439.     if (!PtInRgn(mouse, pWE->viewRgn))
  440.         goto cleanup;
  441.  
  442.     // get drop offset into the text
  443.     WEPointToLongPoint(mouse, &dropLocation);
  444.     insertionOffset = WEGetOffset(&dropLocation, &dropEdge, hWE);
  445.     // destStart/destEnd define the range to highlight at the end of the drag
  446.     destStart = insertionOffset;
  447.  
  448.     // drag originated from this same window?
  449.     if (drag == pWE->currentDrag)
  450.     {
  451.  
  452.         // sourceStart/sourceEnd define the range to delete at the end of the move
  453.         sourceStart = pWE->selStart;
  454.         sourceEnd = pWE->selEnd;
  455.  
  456.         // remember text length before insertion
  457.         delta = pWE->textLength;
  458.  
  459.         // if insertion offset is within the original selection range, abort the drag
  460.         // (*err = dragNotAcceptedErr;*)
  461.         if (_WEOffsetInRange(insertionOffset, dropEdge, sourceStart, sourceEnd))
  462.             goto cleanup;
  463.  
  464.         // if the drag originated from this window, a move,
  465.         // rather than a copy, should be performed
  466.         // Exception: the option key may be held down at mouse-down
  467.         // or mouse-up time to force a copy operation.
  468.  
  469.         isMove = !_WEIsOptionDrag(drag);
  470.         isBackwards = (insertionOffset <= sourceStart);
  471.     } // if intra-window drag
  472.  
  473.     // clear null style
  474.     BCLR(pWE->flags, weFUseNullStyle);
  475.  
  476.     // hide selection highlighting
  477.     _WEHiliteRange(pWE->selStart, pWE->selEnd, hWE);
  478.  
  479.     // increment modification count
  480.     pWE->modCount++;
  481.  
  482.     // if undo support is enabled, create a new action so we'll be able to undo the insertion
  483.     if (BTST(pWE->features, weFUndoSupport))
  484.     {
  485.         WEClearUndo(hWE);
  486.         if (_WENewAction(insertionOffset, insertionOffset, 0, weAKDrag, 0, hWE, &hAction) == noErr)
  487.         {
  488.             _WEPushAction(hAction);
  489.         }
  490.     }
  491.  
  492.     // count items in this drag
  493.     if ((err = CountDragItems(drag, &numDragItems)) != noErr)
  494.         goto cleanup;
  495.  
  496.     for (dragItemIndex = 1; dragItemIndex<=numDragItems; dragItemIndex++)
  497.     {
  498.         // get item reference number for current drag item
  499.         if ((err = GetDragItemReferenceNumber(drag, dragItemIndex, &dragItem)) != noErr)
  500.             goto cleanup;
  501.  
  502.         // see if this drag item contains a text flavor
  503.         err = _WEExtractFlavor(drag, dragItem, kTypeText, &hText, insertionOffset, hWE);
  504.         if (err == noErr)
  505.         {
  506.             if (!BTST(pWE->features, weFMonoStyled))
  507.             {
  508.                 // extract accompanying styles and soup, if any
  509.                 err = _WEExtractFlavor(drag, dragItem, kTypeStyles, &hStyles, insertionOffset, hWE);
  510.                 if ((err != noErr) && (err != badDragFlavorErr))
  511.                     goto cleanup;
  512. #if WASTE_OBJECTS
  513.                 err = _WEExtractFlavor(drag, dragItem, kTypeSoup, &hSoup, insertionOffset, hWE);
  514.                 if ((err != noErr) && (err != badDragFlavorErr))
  515.                     goto cleanup;
  516. #endif
  517.             }
  518.  
  519.             // any extra space added because of intelligent cut-and-paste rules will use the
  520.             // style attributes set at the insertion point
  521.             if (dragItemIndex == 1)
  522.             {
  523.                 pWE->selStart = insertionOffset;
  524.                 pWE->selEnd = insertionOffset;
  525.                 _WESynchNullStyle(hWE);
  526.             }
  527.  
  528.             // get text length
  529.             insertionLength = GetHandleSize(hText);
  530.             destEnd = insertionOffset + insertionLength;
  531.  
  532.             // insert the new text at the insertion point
  533.             HLock(hText);
  534.             err = _WEInsertText(insertionOffset, *hText, insertionLength, hWE);
  535.             _WEForgetHandle(&hText);
  536.             if (err != noErr)
  537.                 goto cleanup;
  538.  
  539.             // adjust deletion range length in undo buffer
  540.             _WEAdjustUndoRange(insertionLength, hWE);
  541.  
  542.             // apply the accompanying styles, if any
  543.             if (hStyles != nil)
  544.             {
  545.                 if ((err = _WEApplyStyleScrap(insertionOffset, destEnd,
  546.                             (StScrpHandle) hStyles, hWE)) != noErr)
  547.                     goto cleanup;
  548.                 _WEForgetHandle(&hStyles);
  549.             }
  550.  
  551. #if WASTE_OBJECTS
  552.             // apply the accompanying soup, if any
  553.             if (hSoup != nil)
  554.             {
  555.                 if ((err = _WEApplySoup(insertionOffset, hSoup, hWE)) != noErr)
  556.                     goto cleanup;
  557.                 _WEForgetHandle(&hSoup);
  558.             }
  559. #endif
  560.  
  561.             // determine whether an extra space should be added before or after the inserted text
  562.             intPasteAction = _WEIntelligentPaste(insertionOffset, destEnd, hWE);
  563.  
  564.             // add the extra space, if necessary
  565.             if (intPasteAction != weDontAddSpaces)
  566.             {
  567.                 if (intPasteAction == weAddSpaceOnLeftSide)
  568.                 {
  569.                     if ((err = _WEInsertText(insertionOffset, &space, sizeof(space), hWE)) != noErr)
  570.                         goto cleanup;
  571.  
  572.                     destEnd++;
  573.  
  574.                     // if an extra space is inserted in front of all dropped items,
  575.                     // don't count it when eventually highlighting the destination range
  576.                     if (dragItemIndex == 1)
  577.                         destStart++;
  578.                 }
  579.                 else
  580.                 {
  581.                     if ((err = _WEInsertText(destEnd, &space, sizeof(space), hWE)) != noErr)
  582.                         goto cleanup;
  583.                 }
  584.  
  585.                 insertionLength++;
  586.                 _WEAdjustUndoRange(1, hWE);
  587.             } // if extra space
  588.  
  589.         }
  590.  
  591. #if WASTE_OBJECTS
  592.         else if (err == badDragFlavorErr)
  593.         {
  594.             SInt32 objectIndex;
  595.             FlavorType objectType;
  596.             Point objectSize;
  597.             SInt16 saveUndoSupport;
  598.             SInt16 saveInhibitRecal;
  599.  
  600.             objectSize.v = 0;
  601.             objectSize.h = 0;
  602.  
  603.             // no text flavor: there must be a flavor matching one of the registered object types
  604.             objectIndex = 0;
  605.             while (_WEGetIndObjectType(objectIndex, &objectType, hWE) == noErr)
  606.             {
  607.                 err = _WEExtractFlavor(drag, dragItem, objectType, &hObjectData, insertionOffset, hWE);
  608.                 if (err == noErr)
  609.                     break; // enclosing while
  610.                 if (err != badDragFlavorErr)
  611.                     goto cleanup;
  612.                 objectIndex++;
  613.             } // while
  614.             if (err != noErr)
  615.                 goto cleanup;
  616.  
  617.             // set insertion point on first iteration (*after* extracting flavors, in case we are
  618.             // doing an intra-window move, otherwise our send proc would be confused)
  619.             if (dragItemIndex == 1)
  620.             {
  621.                 pWE->selStart = insertionOffset;
  622.                 pWE->selEnd = insertionOffset;
  623.             }
  624.  
  625.             // insert the object, but without touching undo or redrawing the text
  626.             saveUndoSupport = WEFeatureFlag(weFUndoSupport, weBitClear, hWE);
  627.             saveInhibitRecal = WEFeatureFlag(weFInhibitRecal, weBitSet, hWE);
  628.             err = WEInsertObject(objectType, hObjectData, objectSize, hWE);
  629.             WEFeatureFlag(weFUndoSupport, saveUndoSupport, hWE);
  630.             WEFeatureFlag(weFInhibitRecal, saveInhibitRecal, hWE);
  631.             if (err != noErr)
  632.                 goto cleanup;
  633.  
  634.             insertionLength = 1;
  635.             destEnd = insertionOffset + 1;
  636.             pWE->modCount--; // compensate for increment made by WEInsertObject
  637.             _WEAdjustUndoRange(1, hWE);
  638.         }
  639. #endif    // WASTE_OBJECTS
  640.  
  641.         else
  642.             goto cleanup;
  643.  
  644.         // advance insertion offset for subsequent drag items, if any
  645.         insertionOffset += insertionLength;
  646.  
  647.     } // for
  648.  
  649.     if (isMove)
  650.     {
  651.         // adjust source range
  652.         if (isBackwards)
  653.         {
  654.             delta -= pWE->textLength;
  655.             sourceStart -= delta;
  656.             sourceEnd -= delta;
  657.         }
  658.  
  659.         // extend range according to intelligent cut-and-paste rules
  660.         _WEIntelligentCut(&sourceStart, &sourceEnd, hWE);
  661.  
  662.         // if undo support is enabled, create a new action so we'll be able to undo the deletion
  663.         if (BTST(pWE->features, weFUndoSupport))
  664.         {
  665.             if (_WENewAction(sourceStart, sourceEnd, 0, weAKDrag, 0, hWE, &hAction) == noErr)
  666.             {
  667.                 _WEPushAction(hAction);
  668.             }
  669.         }
  670.  
  671.         // delete source range
  672.         delta = pWE->textLength;
  673.         if ((err = _WEDeleteRange(sourceStart, sourceEnd, hWE)) != noErr)
  674.             goto cleanup;
  675.  
  676.         // adjust destination range
  677.         if (!isBackwards)
  678.         {
  679.             delta -= pWE->textLength;
  680.             destStart -= delta;
  681.             destEnd -= delta;
  682.         }
  683.  
  684.     } // if isMove
  685.  
  686.     // select the range encompassing all items dropped
  687.     pWE->selStart = destStart;
  688.     pWE->selEnd = destEnd;
  689.  
  690.     // redraw the text
  691.     if (isMove)
  692.         if (sourceStart < destStart)
  693.             err = _WERedraw(sourceStart, destEnd, hWE);
  694.         else
  695.             err = _WERedraw(destStart, sourceEnd, hWE);
  696.     else
  697.         err = _WERedraw(destStart, destEnd, hWE);
  698.  
  699. cleanup:
  700.     // dispose of temporary handles
  701.     _WEForgetHandle(&hText);
  702.     _WEForgetHandle(&hStyles);
  703. #if WASTE_OBJECTS
  704.     _WEForgetHandle(&hSoup);
  705. #endif
  706.  
  707.     // restore the port
  708.     SetPort(savePort);
  709.  
  710.     // unlock the WE record
  711.     _WESetHandleLock((Handle) hWE, saveWELock);
  712.  
  713.     // return result code
  714.     return err;
  715.  
  716. }
  717.  
  718. pascal OSErr _WESendFlavor(FlavorType requestedType, void *dragSendRefCon, WEHandle hWE, DragReference drag)
  719. {
  720. #pragma unused(dragSendRefCon)
  721.  
  722. #if !GENERATINGCFM
  723.     SInt32 saveA5 = SetCurrentA5();    // this fixes a conflict with HoverBar
  724.                                     // (well, probably a bug in the Drag Manager)
  725. #endif
  726.  
  727.     WEPtr pWE = *hWE;
  728.     SInt32 selStart = pWE->selStart;
  729.     SInt32 selEnd = pWE->selEnd;
  730.     Handle hItem = nil;
  731.     Boolean disposeItem = true;        // dispose of item when done
  732. #if WASTE_OBJECTS
  733.     WEObjectDescHandle hObjectDesc;
  734. #endif
  735.     OSErr err;
  736.  
  737.     // allocate a temporary handle to hold a copy of the requested flavor
  738.     if ((err = _WEAllocate(0, kAllocTemp, &hItem)) != noErr)
  739.         goto cleanup;
  740.  
  741. #if WASTE_OBJECTS
  742.     // see if the selection contains an embedded object whose type matches the flavortype
  743.     if (WEGetSelectedObject(&hObjectDesc, hWE) == noErr)
  744.     {
  745.         FlavorType theType;
  746.  
  747.         if ((err = _WEStreamObject(weToDrag, &theType, &hItem, &disposeItem, hObjectDesc)) != noErr)
  748.             goto cleanup;
  749.  
  750.         // make sure theType matches the requested type
  751.         err = badDragFlavorErr;
  752.         if (theType != requestedType)
  753.             goto cleanup;
  754.     }
  755.     else
  756. #endif
  757.     {
  758.         // identify the requested flavor type as either 'TEXT', 'styl' or 'SOUP'
  759.         if (requestedType == kTypeText)
  760.             err = WECopyRange(selStart, selEnd, hItem, nil, nil, hWE);
  761.         else if (requestedType == kTypeStyles)
  762.             err = WECopyRange(selStart, selEnd, nil, hItem, nil, hWE);
  763. #if WASTE_OBJECTS
  764.         else if (requestedType == kTypeSoup)
  765.             err = WECopyRange(selStart, selEnd, nil, nil, hItem,hWE);
  766. #endif
  767.         else
  768.             err = badDragFlavorErr;
  769.  
  770.         if (err != noErr)
  771.             goto cleanup;
  772.  
  773.     }
  774.  
  775.     // set the drag flavor data
  776.     HLock(hItem);
  777.     err = SetDragItemFlavorData(drag, (ItemReference) hWE, requestedType, *hItem,
  778.             GetHandleSize(hItem), 0);
  779.     HUnlock(hItem);
  780.  
  781. cleanup:
  782.     if (disposeItem)
  783.         _WEForgetHandle(&hItem);
  784.  
  785. #if !GENERATINGCFM
  786.     SetA5(saveA5);
  787. #endif
  788.  
  789.     // return result code
  790.     return err;
  791.  
  792. }
  793.  
  794. pascal Boolean WEDraggedToTrash(DragReference drag)
  795. {
  796.  
  797.     // return true if the drop location of the specified drag is the trash
  798.  
  799.     const int bDirectoryAttr = 4;
  800.  
  801.     AEDesc dropLocation, coercedDropLocation;
  802.     CInfoPBRec pb;
  803.     FSSpecPtr pSpec;
  804.     SInt16 trashVRefNum;
  805.     SInt32 trashDirID;
  806.     Boolean draggedToTrash;
  807.  
  808.     draggedToTrash = false;
  809.     dropLocation.dataHandle = nil;
  810.     coercedDropLocation.dataHandle = nil;
  811.  
  812.     // get drop location
  813.     if (GetDropLocation(drag, &dropLocation) != noErr)
  814.         goto cleanup;
  815.  
  816.     // do nothing if dropLocation is a null descriptor
  817.     if (dropLocation.descriptorType == typeNull)
  818.         goto cleanup;
  819.  
  820.     // try to coerce the descriptor to a file system specification record
  821.     if (AECoerceDesc(&dropLocation, typeFSS, &coercedDropLocation) != noErr)
  822.         goto cleanup;
  823.  
  824.     // lock the data handle of the coerced descriptor
  825.     HLock(coercedDropLocation.dataHandle);
  826.     pSpec = *(FSSpecHandle)coercedDropLocation.dataHandle;
  827.  
  828.     // determine the directory ID of the drop location (assuming it's a folder!)
  829.     BLOCK_CLR(pb);
  830.     pb.hFileInfo.ioVRefNum = pSpec->vRefNum;
  831.     pb.hFileInfo.ioDirID = pSpec->parID;
  832.     pb.hFileInfo.ioNamePtr = pSpec->name;
  833.     if (PBGetCatInfoSync(&pb) != noErr)
  834.         goto cleanup;
  835.  
  836.     // make sure the specified file system object is really a directory
  837.     if (!BTST(pb.hFileInfo.ioFlAttrib, bDirectoryAttr))
  838.         goto cleanup;
  839.  
  840.     // find the directory ID of the trash folder
  841.     if (FindFolder(pSpec->vRefNum, kTrashFolderType, kDontCreateFolder, &trashVRefNum, &trashDirID)
  842.         != noErr)
  843.         goto cleanup;
  844.  
  845.     // compare the two directory IDs: if they're the same, the drop location is the trash
  846.     if (pb.dirInfo.ioDrDirID == trashDirID)
  847.         draggedToTrash = true;
  848.  
  849. cleanup:
  850.     // clean up
  851.     AEDisposeDesc(&dropLocation);
  852.     AEDisposeDesc(&coercedDropLocation);
  853.  
  854.     return draggedToTrash;
  855. }
  856.  
  857. pascal RgnHandle _WEOutlineRgn ( RgnHandle solidRgn )
  858. {
  859.     RgnHandle outlineRgn ;
  860.  
  861.     //    make an outline region out of the given solid region
  862.     if ( ( outlineRgn = NewRgn ( ) ) != nil )
  863.     {
  864.         CopyRgn ( solidRgn, outlineRgn ) ;
  865.         InsetRgn ( outlineRgn, 1, 1 ) ;
  866.         DiffRgn ( solidRgn, outlineRgn, outlineRgn ) ;
  867.     }
  868.  
  869.     return outlineRgn ;
  870. }
  871.  
  872. #if WASTE_TRANSLUCENT_DRAGS
  873.  
  874. pascal OSErr _WEMakeDragImage ( GWorldPtr * imageGWorld, RgnHandle * imageRgn, WEHandle hWE )
  875. {
  876.     WEPtr pWE = * hWE ;        //    assume WE record is already locked
  877.     PixMapHandle pixels = nil ;
  878.     Point offset ;
  879.     Rect localBounds ;
  880.     Rect globalBounds ;
  881.     SInt32 dragArea ;
  882.     GDHandle saveDevice ;
  883.     CGrafPtr savePort ;
  884.     GrafPtr textPort ;
  885.     Boolean wasInactive = false ;
  886.     OSErr err ;
  887.  
  888.     //    init return values
  889.     * imageGWorld = nil ;
  890.     * imageRgn = nil ;
  891.  
  892.     //    save current graphics world
  893.     GetGWorld ( & savePort, & saveDevice ) ;
  894.  
  895.     //    set up the port
  896.     textPort = pWE -> port ;
  897.     SetPort ( textPort ) ;
  898.  
  899.     //    calculate delta between global coords and local coords
  900.     offset . v = 0 ;
  901.     offset . h = 0 ;
  902.     LocalToGlobal ( & offset ) ;
  903.  
  904.     //    get image region (in local coordinates)
  905.     if ( ( * imageRgn = WEGetHiliteRgn ( pWE -> selStart, pWE -> selEnd, hWE ) ) == nil )
  906.     {
  907.         err = memFullErr ;
  908.         goto cleanup ;
  909.     }
  910.  
  911.     //    get bounding rectangle of image region
  912.     localBounds = ( ** imageRgn ) -> rgnBBox ;
  913.  
  914.     //    translate to global coordinates
  915.     globalBounds = localBounds ;
  916.     OffsetRect ( & globalBounds, offset . h, offset . v ) ;
  917.  
  918.     //    calculate drag region area
  919.     dragArea = ( ( SInt32 ) globalBounds . right - globalBounds . left ) *
  920.                ( ( SInt32 ) globalBounds . bottom - globalBounds . top ) ;
  921.  
  922.     //    if translucent drags are available and drag region area
  923.     //    is reasonably small, prepare a drag image
  924.     if ( ( dragArea <= pWE -> translucencyThreshold ) && BTST ( pWE -> flags, weFHasTranslucentDrags ) )
  925.     {
  926.  
  927.         //    create an 8-bit deep gworld for drawing the drag image
  928.         if ( ( err = NewGWorld ( imageGWorld, 8, & globalBounds, nil, nil, useTempMem ) ) == noErr )
  929.         {
  930.             //    get the pixmap from the gworld
  931.             pixels = GetGWorldPixMap ( * imageGWorld ) ;
  932.  
  933.             //    set up the gworld
  934.             SetGWorld ( * imageGWorld, nil ) ;
  935.             LockPixels ( pixels ) ;
  936.             SetOrigin ( localBounds . left, localBounds . top ) ;
  937.  
  938.             //    temporarily set weFActive so we'll draw the full highlighting
  939.             if ( ! BTST ( pWE -> flags, weFActive ) )
  940.             {
  941.                 wasInactive = true ;
  942.                 BSET ( pWE -> flags, weFActive ) ;
  943.             }
  944.  
  945.             //    draw the selection into the gworld
  946.             EraseRect ( & localBounds ) ;
  947.             pWE -> port = ( GrafPtr ) * imageGWorld ;
  948.             WEUpdate ( * imageRgn, hWE ) ;
  949.             pWE -> port = textPort ;
  950.  
  951.             //    restore original setting of weFActive
  952.             if ( wasInactive )
  953.             {
  954.                 BCLR ( pWE -> flags, weFActive ) ;
  955.             }
  956.  
  957.             //    end drawing
  958.             UnlockPixels ( pixels ) ;
  959.             SetOrigin ( 0, 0 ) ;
  960.         }
  961.     }
  962.  
  963.     //    convert the image region to global coordinates
  964.     OffsetRgn ( * imageRgn, offset . h, offset . v ) ;
  965.  
  966.     if ( pixels != nil )
  967.     {
  968.         //    convert the image itself to global coordinates
  969.         offset = topLeft ( ( ** imageRgn ) -> rgnBBox ) ;
  970.         OffsetRect ( & ( * pixels ) -> bounds, offset . h, offset . v ) ;
  971.     }
  972.  
  973.     //    clear result code
  974.     err = noErr ;
  975.  
  976. cleanup :
  977.     //    restore original graphics world
  978.     SetGWorld ( savePort, saveDevice ) ;
  979.  
  980.     //    return result code
  981.     return err ;
  982. }
  983.  
  984. #endif    // WASTE_TRANSLUCENT_DRAGS
  985.  
  986. pascal OSErr _WEDrag ( Point mouseLoc, EventModifiers modifiers, UInt32 clickTime, WEHandle hWE )
  987. {
  988.     WEPtr pWE = * hWE ;        //    assume WE record is already locked
  989.     DragReference drag = kNullDrag ;
  990.     RgnHandle dragRgn = nil ;
  991.     RgnHandle dragOutline = nil ;
  992.     EventRecord event ;
  993.     Rect dragBounds ;
  994.     Point tempPoint ;
  995.     GrafPtr savePort ;
  996.     OSErr err ;
  997. #if WASTE_OBJECTS
  998.     WEObjectDescHandle hObjectDesc ;
  999. #endif
  1000. #if WASTE_TRANSLUCENT_DRAGS
  1001.     GWorldPtr dragGWorld = nil ;
  1002. #endif
  1003.  
  1004.     // set up the port
  1005.     GetPort ( & savePort ) ;
  1006.     SetPort ( pWE -> port ) ;
  1007.  
  1008.     // turn the cursor into an arrow
  1009.     SetCursor ( & qd . arrow ) ;
  1010.  
  1011.     // fabricate an EventRecord for TrackDrag
  1012.     event . what = mouseDown ;
  1013.     event . message = 0 ;
  1014.     event . when = clickTime ;
  1015.     event . where = mouseLoc ;
  1016.     LocalToGlobal ( & event . where ) ;
  1017.     event . modifiers = modifiers ;
  1018.  
  1019.     // to start the drag, the user must move the mouse a certain distance
  1020.     // away from the initial mouse location; this allows a short click in
  1021.     // the selection area to set the insertion point
  1022.     err = weNoDragErr ;
  1023.     if ( ! WaitMouseMoved ( event . where ) )
  1024.     {
  1025.         goto cleanup ;
  1026.     }
  1027.  
  1028.     // create a drag object
  1029.     if ( ( err = NewDrag ( & drag ) ) != noErr )
  1030.     {
  1031.         goto cleanup ;
  1032.     }
  1033.  
  1034. #if WASTE_OBJECTS
  1035.     // if the selection range consists of an embedded object,
  1036.     // then use its object type as flavor type
  1037.     if ( WEGetSelectedObject ( & hObjectDesc, hWE ) == noErr )
  1038.     {
  1039.         FlavorType theType ;
  1040.         Handle theData = nil ;
  1041.         Boolean disposeData ;
  1042.  
  1043.         if ( ( err = _WEStreamObject ( weToDrag, & theType, & theData, & disposeData, hObjectDesc ) ) != noErr )
  1044.         {
  1045.             goto cleanup ;
  1046.         }
  1047.  
  1048.         if ( ( err = AddDragItemFlavor ( drag, ( ItemReference ) hWE, theType, nil, 0, 0 ) ) != noErr )
  1049.         {
  1050.             goto cleanup ;
  1051.         }
  1052.     }
  1053.     else
  1054. #endif
  1055.     {
  1056.  
  1057.         // add a 'TEXT' flavor to the drag
  1058.         if ( ( err = AddDragItemFlavor ( drag, ( ItemReference ) hWE, kTypeText, nil, 0, 0 ) ) != noErr )
  1059.         {
  1060.             goto cleanup ;
  1061.         }
  1062.  
  1063.         // add a 'styl' flavor to the drag
  1064.         if ( ( err = AddDragItemFlavor ( drag, ( ItemReference ) hWE, kTypeStyles, nil, 0, 0 ) ) != noErr )
  1065.         {
  1066.             goto cleanup ;
  1067.         }
  1068.  
  1069. #if WASTE_OBJECTS
  1070.         // add a 'SOUP' flavor to the drag
  1071.         if ( ( err = AddDragItemFlavor ( drag, ( ItemReference ) hWE, kTypeSoup, nil, 0, 0 ) ) != noErr )
  1072.         {
  1073.             goto cleanup ;
  1074.         }
  1075. #endif
  1076.  
  1077.     }
  1078.  
  1079.     // since we didn't provide the flavor data for any of the above flavors,
  1080.     // we need supply a data send callback
  1081.     if ( _weFlavorSender == nil )
  1082.     {
  1083.         _weFlavorSender = NewDragSendDataProc ( _WESendFlavor ) ;
  1084.     }
  1085.  
  1086.     if ( ( err = SetDragSendProc ( drag, _weFlavorSender, 0 ) ) != noErr )
  1087.     {
  1088.         goto cleanup ;
  1089.     }
  1090.  
  1091. #if WASTE_TRANSLUCENT_DRAGS
  1092.     //    get drag image & region
  1093.     if ( ( err = _WEMakeDragImage( & dragGWorld, & dragRgn, hWE ) ) != noErr )
  1094.     {
  1095.         goto cleanup ;
  1096.     }
  1097. #else
  1098.     //    get drag region (in local coordinates)
  1099.     if ( ( dragRgn = WEGetHiliteRgn ( pWE -> selStart, pWE -> selEnd, hWE ) ) == nil )
  1100.     {
  1101.         err = memFullErr ;
  1102.         goto cleanup ;
  1103.     }
  1104.  
  1105.     //    convert drag region to global coordinates
  1106.     tempPoint . v = 0 ;
  1107.     tempPoint . h = 0 ;
  1108.     LocalToGlobal ( & tempPoint ) ;
  1109.     OffsetRgn ( dragRgn, tempPoint . h, tempPoint . v ) ;
  1110. #endif
  1111.  
  1112.     //    set the bounds of the drag
  1113.     dragBounds = ( * dragRgn ) -> rgnBBox ;
  1114.     if ( ( err = SetDragItemBounds ( drag, ( ItemReference ) hWE, & dragBounds ) ) != noErr )
  1115.     {
  1116.         goto cleanup ;
  1117.     }
  1118.  
  1119. #if WASTE_TRANSLUCENT_DRAGS
  1120.     //    set drag image, if any
  1121.     if ( dragGWorld != nil )
  1122.     {
  1123.         tempPoint . v = 0 ;
  1124.         tempPoint . h = 0 ;
  1125.         SetDragImage ( drag, GetGWorldPixMap ( dragGWorld ), dragRgn, tempPoint, dragStandardImage ) ;
  1126.     }
  1127. #endif
  1128.  
  1129.     // create an outline region for TrackDrag
  1130.     dragOutline = _WEOutlineRgn ( dragRgn ) ;
  1131.  
  1132.     // stash drag reference in currentDrag so WETrackDrag and WEReceiveDrag
  1133.     // can tell whether a given drag originated from this WE instance
  1134.     pWE -> currentDrag = drag ;
  1135.  
  1136.     // track the drag
  1137.     err = TrackDrag ( drag, & event, dragOutline ) ;
  1138.     pWE -> currentDrag = kNullDrag ;
  1139.     if (err != noErr)
  1140.     {
  1141.         goto cleanup ;
  1142.     }
  1143.  
  1144.     // if the selection was dragged to the trash and the option key wasn't held down
  1145.     // and if the instance is editable, delete the selection
  1146.     if ( ! BTST ( pWE -> features, weFReadOnly ) )
  1147.     {
  1148.         if ( WEDraggedToTrash ( drag ) )
  1149.         {
  1150.             if ( ! _WEIsOptionDrag ( drag ) )
  1151.             {
  1152.                 if ( ( err = WEDelete ( hWE ) ) != noErr )
  1153.                 {
  1154.                     goto cleanup ;
  1155.                 }
  1156.             }
  1157.         }
  1158.     }
  1159.  
  1160.     // clear result code
  1161.     err = noErr ;
  1162.  
  1163. cleanup:
  1164.     // dispose of the drag
  1165.     if ( drag != kNullDrag )
  1166.     {
  1167.         DisposeDrag ( drag ) ;
  1168.     }
  1169.  
  1170. #if WASTE_TRANSLUCENT_DRAGS
  1171.     //    dispose of the image gworld
  1172.     if ( dragGWorld != nil )
  1173.     {
  1174.         DisposeGWorld ( dragGWorld ) ;
  1175.     }
  1176. #endif
  1177.  
  1178.     //    dispose of auxiliary regions
  1179.     if ( dragRgn != nil )
  1180.     {
  1181.         DisposeRgn ( dragRgn ) ;
  1182.     }
  1183.     if ( dragOutline != nil )
  1184.     {
  1185.         DisposeRgn ( dragOutline ) ;
  1186.     }
  1187.  
  1188.     // restore the port
  1189.     SetPort ( savePort ) ;
  1190.  
  1191.     // return result code
  1192.     return err;
  1193. }
  1194.  
  1195. #if WASTE_IC_SUPPORT
  1196.  
  1197. pascal void _WEResolveURL(EventModifiers modifiers, SInt32 urlStart, SInt32 urlEnd, WEHandle hWE)
  1198. {
  1199.     WEPtr pWE = *hWE;            // assume WE record is already locked
  1200.     Str255 hint;
  1201.     FourCharCode signature;
  1202.     ProcessSerialNumber psn;
  1203.     ProcessInfoRec info;
  1204.     ICInstance inst;
  1205.     ICError err;
  1206.     SInt32 junkLong;
  1207.     Handle hURL = nil;
  1208.     Boolean saveTextLock;
  1209.  
  1210.     // get the hint string IC will use to parse slack URLs
  1211.     hint[0] = 0;
  1212.     if (pWE->hURLHint != nil)
  1213.     {
  1214.         BlockMoveData(*pWE->hURLHint, hint, StrLength(*pWE->hURLHint) + 1);
  1215.     }
  1216.  
  1217.     // find out the signature of the current process
  1218.     signature = '¥?¥?¥?¥?';
  1219.     psn.highLongOfPSN = 0;
  1220.     psn.lowLongOfPSN = 0;
  1221.     if (GetCurrentProcess(&psn) == noErr)
  1222.     {
  1223.         BLOCK_CLR(info);
  1224.         info.processInfoLength = sizeof(info);
  1225.         if (GetProcessInformation(&psn, &info) == noErr)
  1226.             signature = info.processSignature;
  1227.     }
  1228.  
  1229.     if (ICStart(&inst, signature) == noErr)
  1230.     {
  1231.         if (ICFindConfigFile(inst, 0, nil) == noErr)
  1232.         {
  1233.             saveTextLock = _WESetHandleLock(pWE->hText, true);
  1234.  
  1235.             // cmd + option click just highlights the URL,
  1236.             // without actually resolving it
  1237.             if (modifiers & optionKey)
  1238.             {
  1239.                 hURL = NewHandle(0);
  1240.                 err = ICParseURL(inst, hint, *pWE->hText, WEGetTextLength(hWE), &urlStart, &urlEnd, hURL);
  1241.                 _WEForgetHandle(&hURL);
  1242.             }
  1243.             else
  1244.             {
  1245.                 err = ICLaunchURL(inst, hint, *pWE->hText, WEGetTextLength(hWE), &urlStart, &urlEnd);
  1246.             }
  1247.             _WESetHandleLock(pWE->hText, saveTextLock);
  1248.             WESetSelection(urlStart, urlEnd, hWE);
  1249.  
  1250.             // flash selection if successful (unless option key was down)
  1251.             if ((err == noErr) && !(modifiers & optionKey))
  1252.             {
  1253.                 Delay(5, &junkLong);
  1254.                 WEDeactivate(hWE);
  1255.                 Delay(5, &junkLong);
  1256.                 WEActivate(hWE);
  1257.             }
  1258.         }
  1259.         ICStop(inst);
  1260.     }
  1261. }
  1262.  
  1263. #endif    // WASTE_IC_SUPPORT
  1264.  
  1265. pascal void WEClick(Point mouseLoc, EventModifiers modifiers, UInt32 clickTime, WEHandle hWE)
  1266. {
  1267.     WEPtr pWE;
  1268.     LongPt thePoint;
  1269.     SInt32 offset, anchor;
  1270.     SInt32 rangeStart, rangeEnd;
  1271.     WEEdge edge;
  1272.     Boolean isMultipleClick;
  1273.     Boolean saveWELock;
  1274. #if WASTE_OBJECTS
  1275.     WEObjectDescHandle hObjectDesc;
  1276. #endif
  1277. #if WASTE_IC_SUPPORT
  1278.     SInt32 urlStart, urlEnd;
  1279. #endif
  1280.  
  1281.     // stop any ongoing inline input session
  1282.     WEStopInlineSession(hWE);
  1283.  
  1284.     // lock the WE record
  1285.     saveWELock = _WESetHandleLock((Handle) hWE, true);
  1286.     pWE = *hWE;
  1287.  
  1288. #if WASTE_IC_SUPPORT
  1289.     // remember the selection range before the click
  1290.     urlStart = pWE->selStart;
  1291.     urlEnd = pWE->selEnd;
  1292. #endif
  1293.  
  1294.     // hide the caret if it's showing
  1295.     if (BTST(pWE->flags, weFCaretVisible))
  1296.         _WEBlinkCaret(hWE);
  1297.  
  1298.     // find click offset
  1299.     WEPointToLongPoint(mouseLoc, &thePoint);
  1300.     offset = WEGetOffset(&thePoint, &edge, hWE);
  1301.  
  1302.     // determine whether this click is part of a sequence
  1303.     // a single click inside an object selects it, so it's like a double click in a word
  1304.     isMultipleClick = ((clickTime < pWE->clickTime + GetDoubleClickTime()) && (offset == pWE->clickLoc));
  1305.  
  1306.     // remember click time, click offset and edge value
  1307.     pWE->clickTime = clickTime;
  1308.     pWE->clickLoc = offset;
  1309.     pWE->clickEdge = edge;
  1310.  
  1311. #if WASTE_OBJECTS
  1312.     // when selected, embedded objects can intercept mouse clicks
  1313.     if (WEGetSelectedObject(&hObjectDesc, hWE) == noErr)
  1314.         if (_WEOffsetInRange(offset, edge, pWE->selStart, pWE->selEnd))
  1315.             if (_WEClickObject(mouseLoc, modifiers + isMultipleClick, clickTime, hObjectDesc))
  1316.                     goto cleanup;
  1317. #endif
  1318.  
  1319.     if ((modifiers & shiftKey) == 0)
  1320.     {
  1321.  
  1322.         // is this click part of a sequence or is it a single click?
  1323.         if (isMultipleClick)
  1324.         {
  1325.             pWE->clickCount++;
  1326.  
  1327.             // a double (triple) click creates an anchor-word (anchor-line)
  1328.             if (pWE->clickCount > 1)
  1329.                 WEFindLine(offset, edge, &pWE->anchorStart, &pWE->anchorEnd, hWE);
  1330.             else
  1331.                 WEFindWord(offset, edge, &pWE->anchorStart, &pWE->anchorEnd, hWE);
  1332.  
  1333.             offset = pWE->anchorStart;
  1334.         }
  1335.         else
  1336.         {
  1337.             // single-click
  1338.             // if the Drag Manager is available and the click went in the selection range,
  1339.             // this click may be the beginning of a drag gesture
  1340.             if (BTST(pWE->flags, weFHasDragManager) && BTST(pWE->features, weFDragAndDrop))
  1341.                 if (_WEOffsetInRange(offset, edge, pWE->selStart, pWE->selEnd))
  1342.                     if (_WEDrag(mouseLoc, modifiers, clickTime, hWE) != weNoDragErr)
  1343.                         goto cleanup;
  1344.  
  1345.             pWE->clickCount = 0;
  1346.             anchor = offset;
  1347.         }
  1348.     }
  1349.     else
  1350.     {
  1351.  
  1352.         // if the shift key was down, use the old anchor offset found with the previous click
  1353.         anchor = BTST(pWE->flags, weFAnchorIsEnd) ? pWE->selEnd : pWE->selStart;
  1354.     }
  1355.  
  1356.     // set the weFMouseTracking bit while we track the mouse
  1357.     BSET(pWE->flags, weFMouseTracking);
  1358.  
  1359.     // MOUSE TRACKING LOOP
  1360.     do
  1361.     {
  1362.  
  1363.         // get text offset corresponding to mouse position
  1364.         WEPointToLongPoint(mouseLoc, &thePoint);
  1365.         offset = WEGetOffset(&thePoint, &edge, hWE);
  1366.  
  1367.         // if we're selecting words or lines, pin offset to a word or line boundary
  1368.         if (pWE->clickCount > 0)
  1369.         {
  1370.             if (pWE->clickCount > 1)
  1371.                 WEFindLine(offset, edge, &rangeStart, &rangeEnd, hWE);
  1372.             else
  1373.                 WEFindWord(offset, edge, &rangeStart, &rangeEnd, hWE);
  1374.  
  1375.             // choose the word/line boundary and the anchor that are farthest away from each other
  1376.             if (offset > pWE->anchorStart)
  1377.             {
  1378.                 anchor = pWE->anchorStart;
  1379.                 offset = rangeEnd;
  1380.             }
  1381.             else
  1382.             {
  1383.                 offset = rangeStart;
  1384.                 anchor = pWE->anchorEnd;
  1385.             }
  1386.         }
  1387.         else
  1388.         {
  1389.             // if the point is in the middle of an object, the selection should include it
  1390.             if (edge == kObjectEdge)
  1391.                 offset++;
  1392.         }
  1393.  
  1394.         // set the selection range from anchor point to current offset
  1395.         WESetSelection(anchor, offset, hWE);
  1396.  
  1397.         // call the click loop callback, if any
  1398.         if (pWE->clickLoop != nil)
  1399.             if (!CallWEClickLoopProc(hWE, pWE->clickLoop))
  1400.                 break;
  1401.  
  1402.         // update mouse position
  1403.         GetMouse(&mouseLoc);
  1404.  
  1405.     } while(WaitMouseUp());
  1406.  
  1407.     // clear the weFMouseTracking bit
  1408.     BCLR(pWE->flags, weFMouseTracking);
  1409.  
  1410.     // redraw the caret immediately if the selection range is empty
  1411.     if (anchor == offset)
  1412.         _WEBlinkCaret(hWE);
  1413.  
  1414. #if WASTE_IC_SUPPORT
  1415.     if (modifiers & cmdKey)
  1416.     {
  1417.         // command+clicking a URL tries to resolve it
  1418.         // we normally ask IC to parse the text surrounding the clicked point,
  1419.         // but if a selection already existed prior to the click, we pass
  1420.         // that to IC rather than forcing a re-parse
  1421.         if ((anchor != offset) || (anchor < urlStart) || (anchor > urlEnd))
  1422.         {
  1423.             urlStart = anchor;
  1424.             urlEnd = offset;
  1425.         }
  1426.         _WEResolveURL(modifiers, urlStart, urlEnd, hWE);
  1427.     }
  1428. #endif
  1429.  
  1430. cleanup:
  1431.     // unlock the WE record
  1432.     _WESetHandleLock((Handle) hWE, saveWELock);
  1433.  
  1434. }
  1435.  
  1436. pascal UInt16 WEGetClickCount(WEHandle hWE)
  1437. {
  1438.     return (*hWE)->clickCount;
  1439. }
  1440.